/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "ina.h"
#include <mc/exception.h>
#include <mc/log.h>

using namespace dev;

#define REG_WIDTH 2
#define REG_MASK 0xFFFF
#define CONFIG_REG_OFFSET 0x00

std::unordered_map<std::string, uint32_t> CALIBRATION_REG_OFFSET = {
    {"Ina220", 0x05},
    {"Ina226", 0x05},
    {"Ina238", 0x02}
};

std::unordered_map<std::string, uint8_t> INPUT_MAX_LEN = {
    {"Ina220", 2},
    {"Ina226", 2},
    {"Ina238", 3}
};

// 针对INA芯片寄存器，根据数据长度进行大小端转换
static inline void convert_buf(const std::vector<uint8_t>& src, 
                               std::vector<uint8_t>& dst, uint8_t len)
{
    if (len == 0) {
        return;
    }

    if (len == 1) {
        dst[0] = src[0];
        return;
    }

    for (uint8_t i = 0; i < len; ++i) {
        dst[i] = src[len - 1 - i];
    }
}

bool chip_ina::init(mc::mutable_dict& csr_object) {
    try {
        if (!chip_base::init(csr_object)) {
            MC_THROW(mc::invalid_op_exception, "Chip base init failed");
        }

        if (csr_object.contains("ConfigurationRegister")) {
            m_config_reg = csr_object["ConfigurationRegister"].as<uint16_t>();
        } else {
            return false;
        }
        
        if (csr_object.contains("CalibrationRegister")) {
            m_calibration_reg = csr_object["CalibrationRegister"].as<uint16_t>();
        } else {
            return false;
        }

        if (csr_object.contains("ChipModel")) {
            m_chip_model = csr_object["ChipModel"].as<std::string>();
        } else {
            return false;
        }

    } catch (const std::exception& e) {
        elog("Ina initialization failed: ${error}", ("error", e.what()));
        return false;
    }

    return true;
}

int32_t chip_ina::chip_config(void) {
    try {
        if (m_config_reg == 0x7ff || m_calibration_reg == 0) {
            return HAS_ERR;
        }
    
        int32_t              ret;
        std::string          error;
        std::vector<uint8_t> empty;
        std::vector<uint8_t> cfg_buf(REG_WIDTH, 0);
        std::vector<uint8_t> cal_buf(REG_WIDTH, 0);
    
        cfg_buf[0] = (m_config_reg >> 8) & 0xFF;
        cfg_buf[1] = m_config_reg & 0xFF;
        cal_buf[0] = (m_calibration_reg >> 8) & 0xFF;
        cal_buf[1] = m_calibration_reg & 0xFF;
        
        ret = chip_base::bit_write(CONFIG_REG_OFFSET, REG_WIDTH, REG_MASK, cfg_buf, empty, error);
        if (ret != HAS_OK) {
            elog("INA config register write failed: ${error}", ("error", error));
            return HAS_ERR;
        }

        uint32_t cal_offset = CALIBRATION_REG_OFFSET[m_chip_model];
        ret = chip_base::bit_write(cal_offset, REG_WIDTH, REG_MASK, cal_buf, empty, error);
        if (ret != HAS_OK) {
            elog("INA calibration register write failed: ${error}", ("error", error));
            return HAS_ERR;
        }

        m_is_initialized = true;
    } catch (const std::exception& e) {
        elog("Exception during INA initialization: ${error}", ("error", e.what()));
        return HAS_ERR;
    }

    return HAS_OK;
}

int32_t chip_ina::bit_read(uint32_t offset, uint8_t length, uint32_t mask,
                           std::vector<uint8_t>& in_buffer, std::vector<uint8_t>& out_buffer,
                           std::string& error) {
    try {
        int32_t ret;
        if (length > INPUT_MAX_LEN[m_chip_model]) {
            error = "INA request error: length exceeds maximum allowed value";
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        if (!m_is_initialized) {
            ret = chip_config();
            if (ret != HAS_OK) {
                error = "INA chip config failed";
                return ret;
            }
        }

        std::vector<uint8_t> converted_buf(length);
        ret = chip_base::bit_read(offset, length, mask, in_buffer, converted_buf, error);
        if (ret != HAS_OK) {
            elog("INA bit_read failed: ${error}", ("error", error));
            return ret;
        }

        out_buffer.resize(length);
        convert_buf(converted_buf, out_buffer, length);

        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("INA bit_read exception: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}


int32_t chip_ina::bit_write(uint32_t offset, uint8_t length, uint32_t mask,
                            const std::vector<uint8_t>& in_buffer,
                            std::vector<uint8_t>&       out_buffer, std::string& error) {
    try {
        int32_t ret;
        if (length > INPUT_MAX_LEN[m_chip_model]) {
            error = "INA request error: length exceeds maximum allowed value";
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        if (in_buffer.size() < length) {
            error = "INA request error: input buffer size is too small";
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        if (!m_is_initialized) {
            ret = chip_config();
            if (ret != HAS_OK) {
                error = "INA chip config failed";
                return ret;
            }
        }

        std::vector<uint8_t> converted_buf(length);
        convert_buf(in_buffer, converted_buf, length);

        ret = chip_base::bit_write(offset, length, mask, converted_buf, out_buffer, error);
        if (ret != HAS_OK) {
            elog("INA bit_write failed: ${error}", ("error", error));
            return ret;
        }

        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("INA bit_write exception: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_ina::block_read(uint32_t offset, uint32_t length, std::vector<uint8_t>& in_buffer,
                             std::vector<uint8_t>& out_buffer, std::string& error) {
    try {
        int32_t ret;
        if (length > INPUT_MAX_LEN[m_chip_model]) {
            error = "INA request error: length exceeds maximum allowed value";
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        if (!m_is_initialized) {
            ret = chip_config();
            if (ret != HAS_OK) {
                error = "INA chip config failed";
                return ret;
            }
        }

        std::vector<uint8_t> converted_buf(length);
        ret = chip_base::block_read(offset, length, in_buffer, converted_buf, error);
        if (ret != HAS_OK) {
            elog("INA block_read failed: ${error}", ("error", error));
            return ret;
        }

        out_buffer.resize(length);
        convert_buf(converted_buf, out_buffer, length);

        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("INA block_read exception: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_ina::block_write(uint32_t offset, const std::vector<uint8_t>& in_buffer,
                               std::vector<uint8_t>& out_buffer, std::string& error) {
    try {
        int32_t ret;
        int32_t length = in_buffer.size();
        if (length > INPUT_MAX_LEN[m_chip_model]) {
            error = "INA request error: length exceeds maximum allowed value";
            elog("${error}", ("error", error));
            return HAS_ERR;
        }

        if (!m_is_initialized) {
            ret = chip_config();
            if (ret != HAS_OK) {
                error = "INA chip config failed";
                return ret;
            }
        }

        std::vector<uint8_t> converted_buf(length);
        convert_buf(in_buffer, converted_buf, length);

        ret = chip_base::block_write(offset, converted_buf, out_buffer, error);
        if (ret != HAS_OK) {
            elog("INA block_write failed: ${error}", ("error", error));
            return ret;
        }

        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("INA block_write exception: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

